package com.tpvlog.dfs.backupnode.file;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.tpvlog.dfs.backupnode.log.CheckPoint;
import com.tpvlog.dfs.backupnode.log.FSImage;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 负责管理元数据的核心组件
 *
 * @author Ressmix
 */
public class FSNameSystem {

    // 负责管理内存文件目录树的组件
    private FSDirectory directory;

    // Checkpoint检查点
    private CheckPoint checkPoint = new CheckPoint();

    // 是否正在从fsimage恢复元数据
    private volatile boolean finishedRecover = false;

    public FSNameSystem() {
        this.directory = new FSDirectory();
        // 从fsimage恢复元数据
        recoverNamespace();
    }

    /**
     * 创建目录
     *
     * @param path 目录路径
     * @return 是否成功
     */
    public Boolean mkdir(Long txid, String path) {
        directory.mkdir(txid, path);
        return true;
    }

    /**
     * 创建文件
     */
    public Boolean ceateFile(long txid, String filename) throws Exception {
        if (!directory.ceateFile(txid, filename)) {
            return false;
        }
        return true;
    }

    /**
     * 获取文件目录树的json
     */
    public FSImage getFSImage() {
        return directory.getFSImage();
    }

    /**
     * 获取当前已经同步的最大txid
     */
    public long getSyncedTxid() {
        return directory.getMaxTxid();
    }

    /**
     * 恢复元数据
     */
    public void recoverNamespace() {
        try {
            loadCheckpointInfo();
            loadFSImage();
            finishedRecover = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载checkpoint文件
     */
    private void loadCheckpointInfo() throws Exception {
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            String path = "C:\\Users\\Ressmix\\Desktop\\backupnode\\checkpoint-info.meta";
            File file = new File(path);
            if (!file.exists()) {
                System.out.println("checkpoint info文件不存在，不进行恢复.......");
                return;
            }

            in = new FileInputStream(path);
            channel = in.getChannel();

            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            buffer.flip();

            // 解析checkpoint
            String checkpointInfo = new String(buffer.array(), 0, count);
            long checkpointTime = Long.valueOf(checkpointInfo.split("_")[0]);
            long syncedTxid = Long.valueOf(checkpointInfo.split("_")[1]);
            String fsimageFile = checkpointInfo.split("_")[2];
            System.out.println("恢复checkpoint time：" + checkpointTime + ", synced txid: " + syncedTxid + ", fsimage file: " + fsimageFile);

            this.checkPoint.setCheckpointTime(checkpointTime);
            this.checkPoint.setFsimageFile(fsimageFile);
            this.checkPoint.setSyncedTxid(syncedTxid);
            directory.setMaxTxid(syncedTxid);
        } finally {
            if (in != null) {
                in.close();
            }
            if (channel != null) {
                channel.close();
            }
        }
    }

    /**
     * 加载fsimage文件到内存里来进行恢复
     */
    private void loadFSImage() throws Exception {
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            String fsimage = checkPoint.getFsimageFile();
            String path = "C:\\Users\\Ressmix\\Desktop\\backupnode\\" + fsimage;
            File file = new File(path);
            if (!file.exists()) {
                System.out.println("fsimage文件当前不存在，不进行恢复.......");
                return;
            }

            in = new FileInputStream(path);
            channel = in.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
            int count = channel.read(buffer);

            buffer.flip();
            String fsimageJson = new String(buffer.array(), 0, count);
            System.out.println("恢复fsimage文件中的数据：" + fsimageJson);

            FSDirectory.INode dirTree = JSONObject.parseObject(fsimageJson, new TypeReference<FSDirectory.INode>() {
            });
            System.out.println(dirTree);

            // 恢复目录树
            directory.setDirTree(dirTree);
        } finally {
            if (in != null) {
                in.close();
            }
            if (channel != null) {
                channel.close();
            }
        }
    }

    public boolean isFinishedRecover() {
        return finishedRecover;
    }

    public void setFinishedRecover(boolean finishedRecover) {
        this.finishedRecover = finishedRecover;
    }

    public CheckPoint getCheckPoint() {
        return checkPoint;
    }

    public void setCheckPoint(CheckPoint checkPoint) {
        this.checkPoint = checkPoint;
    }
}
